package com.humanminoritygame.schedule;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Random;
import java.util.Set;

import org.hibernate.SessionFactory;
import org.springframework.mail.MailException;
import org.springframework.mail.MailSender;
import org.springframework.mail.SimpleMailMessage;

import com.humanminoritygame.model.AnswerType;
import com.humanminoritygame.model.Category;
import com.humanminoritygame.model.ResultSummary;
import com.humanminoritygame.model.Room;
import com.humanminoritygame.model.RoomRepo;
import com.humanminoritygame.model.Round;
import com.humanminoritygame.model.Subscription;
import com.humanminoritygame.model.SubscriptionRepo;
import com.humanminoritygame.model.User;
import com.humanminoritygame.model.UserRepo;
import com.humanminoritygame.model.UserRoundResult;
import com.humanminoritygame.model.UserRoundResultRepo;
import com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException;

public class RoundsThread implements Runnable{
  private RoomRepo roomRepo;
  private UserRoundResultRepo userRoundResultRepo;
  private SessionFactory sessionFactory;
  private UserRepo userRepo;
  private SubscriptionRepo subscriptionRepo;
  private MailSender mailSender;
  private SimpleMailMessage templateMessage;
 
  
  public RoundsThread(MailSender mailSender, SimpleMailMessage templateMessage, SessionFactory sessionFactory, RoomRepo roomRepo, UserRoundResultRepo userRoundResultRepo, UserRepo userRepo, SubscriptionRepo subscriptionRepo){
    this.roomRepo = roomRepo;
    this.userRoundResultRepo = userRoundResultRepo;
    this.sessionFactory = sessionFactory;
    this.userRepo = userRepo;
    this.subscriptionRepo = subscriptionRepo;
    this.mailSender = mailSender;
    this.templateMessage = templateMessage;
  }
  
  public void run() {
    try{
      sendMail("Thread Is Up Again! :)");
      while(true){
        int sleepingTime = Category.FASTEST_CATEGORY;
        sessionFactory.getCurrentSession().beginTransaction();
        Set<Room> rooms = roomRepo.getOpenRooms();
        for(Room room :  rooms){
          Round round = room.getLastRound(); 
          if(round != null && round.hasElapsed()){
            doForOne(room, round);
          }else{
            sleepingTime = Math.min(round.getRemainingTime(), sleepingTime);
          }
        }
        sessionFactory.getCurrentSession().getTransaction().commit();
        Thread.sleep(sleepingTime+500);
      }
    } catch (Exception exc){
      sendMail(exc);
      
      //We don't want this thread to die EVER
      RoundsThread rt = new RoundsThread(mailSender, templateMessage, sessionFactory, roomRepo, userRoundResultRepo, userRepo, subscriptionRepo);
      Thread newThrd = new Thread(rt);
      newThrd.start();
      return;
    }
  }

  public void doForOne(Room room, Round round) {
    ResultSummary result = round.getSummary();
    if( result== null || result.getAnswer()!=null ){
      robotVotes(room, round);
      userRoundResultRepo.processResults(round);
    }
    if(room.getRoundQty() < Room.MAXROUNDS){
      sessionFactory.getCurrentSession().saveOrUpdate(room.addRound());
    }else{
      room.close();
    }
  }
  
  private void robotVotes(Room r, Round round) {
    Set<User> robots = userRepo.getRobots();
    int i = 3;
    for(User robot : robots){
      makeRobotVote(robot, r, round, i);
      i= i+2;
    }
  }
  
  private void makeRobotVote(User user, Room room, Round round, int window){
    Subscription subs = subscriptionRepo.getSubscription(user, room);
    if(!userRoundResultRepo.hasVoted(subs, round)){
      userRoundResultRepo.addUserRoundResult(new UserRoundResult(decideVote(room, window), subs, round));
    }
  }

  private AnswerType decideVote(Room room, int window) {
    Set<Round> previousRounds = roomRepo.getRoomById(room.getId()).getLastWindowRounds(window);
    if(previousRounds.size() == 0){
      return new Random().nextBoolean()? AnswerType.A : AnswerType.B;
    }
    int as=0, bs=0;
    for(Round r : previousRounds){
      if(r.getSummary()!=null && r.getSummary().getAnswer() == AnswerType.A){
        as++;
      } else {
        bs++;
      }
    }
    return as<bs? AnswerType.B : AnswerType.A;
  }
  
  private void sendMail(String message){
    SimpleMailMessage msg =  new SimpleMailMessage(this.templateMessage);
    msg.setSubject("[HMG-error] - RoundsThread is up");
    msg.setText("Message: " 
        + message);
    try{
      this.mailSender.send(msg);
    } catch(MailException ex) {
      // simply log it and go on...
     // log.fatal("Email Notification message could not sent", ex);
      throw new RuntimeException(ex);
    }
  }
  
  private void sendMail(Exception exception) {
    SimpleMailMessage msg =  new SimpleMailMessage(this.templateMessage);
    msg.setSubject("[HMG-error] - RoundsThread");
    StringWriter stackTrace = new StringWriter();
    exception.printStackTrace(new PrintWriter(stackTrace));
    msg.setText("Exception!: " 
        + exception.getMessage()
        +"\n Strack Trace: \n"
        + stackTrace.toString());
    try{
      this.mailSender.send(msg);
    } catch(MailException ex) {
      // simply log it and go on...
     // log.fatal("Email Notification message could not sent", ex);
    //  System.out.println("send notification EXCEPTION!!!");
      throw new RuntimeException(ex);
    }
}
  
  
}
